---
title: Client Awareness and Enforcement
subtitle: Require client details and operation names to help monitor schema usage
description: Improve GraphQL operation monitoring by tagging operations with with client details. See code examples for Apollo GraphOS Router and Apollo Server.
published: 2022-05-31
id: TN0001
tags: [server, observability, router]
redirectFrom:
  - /technotes/TN0001-client-id-enforcement/
  - /graphos/routing/observability/client-awareness
  - /graphos/routing/observability/client-id-enforcement
---

Metrics about GraphQL schema usage are more insightful when information about clients using the schema is available. Understanding client usage can help you reshape your schema to serve clients more efficiently.
As part of GraphOS Studio metrics reporting, servers can [tag reported operations with the requesting client's name and version](/graphos/metrics/client-awareness).
This **client awareness** helps graph maintainers understand which clients are using which fields in the schema.

Apollo's GraphOS Router and Apollo Server can enable client awareness by requiring metadata about requesting clients.
The router supports client awareness by default. If the client sets its name and version with the headers `apollographql-client-name` and `apollographql-client-version` in its HTTP requests, GraphOS Studio can separate the metrics and operations per client. 

<Note>

The client name is also used by the persisted queries feature.

</Note>


Clients should [name their GraphQL operations](/react/data/operation-best-practices/#name-all-operations) to provide more context around how and where data is being used.

## Why enforce client reporting?

Client metadata enables better insights into schema usage, such as:

- **Identifying which clients use which fields**: This facilitates usage monitoring and safe deprecation of fields.
- **Understanding traffic patterns**: This helps optimize schema design based on real-world client behavior.
- **Improving operation-level observability**: This provides details for debugging and performance improvements.

Apollo strongly recommends requiring client name, client version, and operation names in all incoming GraphQL requests.

## Customizing client awareness information

The GraphOS Router supports client awareness by default if the client sets the `apollographql-client-name` and `apollographql-client-id` in their requests.
These values can be overridden using the [router configuration file](/router/managed-federation/client-awareness/) directly.
You can use a Rhai script to _enforce_ that clients include metadata.

### Via router configuration

If headers with customized names need to be sent by a browser, they must be allowed in the [CORS (Cross Origin Resource Sharing) configuration](/router/configuration/cors), as follows:

```yaml title="router.yaml"
telemetry:
  apollo:
    # defaults to apollographql-client-name
    client_name_header: MyClientHeaderName
    # defaults to apollographql-client-version
    client_version_header: MyClientHeaderVersion
cors:
  # The headers to allow.
  # (Defaults to [ Content-Type ], which is required for GraphOS Studio)
  allow_headers: [ Content-Type, MyClientHeaderName, MyClientHeaderVersion]
```

### Via router customization

The client awareness headers are parsed out of the HTTP request immediately in the router request lifecycle. If you need to dynamically set the values, you should instead mutate the values in the [GraphQL context](/graphos/routing/customization/overview#request-context) after they have been parsed, but before they are sent. You can do so by using Rhai scripts or coprocessors and hooking into the `RouterRequest` lifecycle stage.

```rhai title="client-name-version.rhai"
fn router_service(service) {
    const request_callback = Fn("process_request");
    service.map_request(request_callback);
}

fn process_request(request) {
    // ... logic to parse request and calculate name/version
    request.context["apollo::telemetry::client_name"] = "custom name";
    request.context["apollo::telemetry::client_version"] = "custom version";
}
```

### Enforcing via Rhai script

Client headers can be enforced using a [Rhai script](/graphos/routing/customization/rhai) on every incoming request.

```rhai title="client-id.rhai"
fn supergraph_service(service) {
    const request_callback = Fn("process_request");
    service.map_request(request_callback);
  }

fn process_request(request) {
  log_info("processing request");
  let valid_clients = ["1", "2"];
  let valid_client_names = ["apollo-client"];

  if ("apollographql-client-version" in request.headers && "apollographql-client-name" in request.headers) {
    let client_header = request.headers["apollographql-client-version"];
    let name_header = request.headers["apollographql-client-name"];

    if !valid_clients.contains(client_header) {
      log_error("Invalid client ID provided");
      throw #{
        status: 401,
        message: "Invalid client ID provided"
      };
    }
    if !valid_client_names.contains(name_header) {
      log_error("Invalid client name provided");
      throw #{
        status: 401,
        message: "Invalid client name provided"
      };
    }
  }
  else {
    log_error("No client headers set");
    throw #{
      status: 401,
      message: "No client headers set"
    };
  }
}
```

See a runnable example Rhai script in the [Apollo Solutions repository](https://github.com/apollosolutions/example-rhai-client-id-validation).

<SolutionsNote />
<Tip>

If you're an enterprise customer looking for more material on this topic, try the [Enterprise best practices: Router extensibility](https://www.apollographql.com/tutorials/router-extensibility) course on Odyssey.

Not an enterprise customer? [Learn about GraphOS for Enterprise.](https://www.apollographql.com/pricing)

</Tip>

## Enforcing headers in Apollo Server

If you're using Apollo Server for your gateway, you can require client metadata in every incoming request with a [custom plugin](/apollo-server/integrations/plugins/):

<Tip>

The header names used below are the default headers sent by Apollo Client, but you can change them to whatever names your client uses. Additionally, these changes must be reflected in the [usage reporting plugin](/apollo-server/api/plugin/usage-reporting/#generateclientinfo) to report client headers to GraphOS. For an example, see [using custom client id headers](/apollo-server/monitoring/metrics#using-custom-headers).

</Tip>

<MultiCodeBlock>

```ts title="index.ts"
function clientEnforcementPlugin(): ApolloServerPlugin<BaseContext> {
  return {
    async requestDidStart() {
      return {
        async didResolveOperation(requestContext) {
          const clientName = requestContext.request.http.headers.get("apollographql-client-name");
          const clientVersion = requestContext.request.http.headers.get("apollographql-client-version");

          if (!clientName) {
            const logString = `Execution Denied: Operation has no identified client`;
            requestContext.logger.debug(logString);
            throw new GraphQLError(logString);
          }

          if (!clientVersion) {
            const logString = `Execution Denied: Client ${clientName} has no identified version`;
            requestContext.logger.debug(logString);
            throw new GraphQLError(logString);
          }

          if (!requestContext.operationName) {
            const logString = `Unnamed Operation: ${requestContext.queryHash}. All operations must be named`;
            requestContext.logger.debug(logString);

            throw new GraphQLError(logString);
          }
        },
      };
    },
  };
}
const server = new ApolloServer({
  typeDefs,
  resolvers,
  plugins: [clientEnforcementPlugin()],
});
```

</MultiCodeBlock>

## Adding enforcement for existing clients

If clients are already consuming your graph and are not providing client metadata, adding universal enforcement will break those clients. To resolve this you should take the following steps:

### Use other headers

If you have other existing headers in your HTTP requests that can be parsed to extract some client info, you can extract the info from there.

#### GraphOS Router

Client awareness headers should be overridden using the [router configuration file](/router/managed-federation/client-awareness/#overriding-client-awareness-headers) to use the appropriate header names.

#### Apollo Server

If you do change the identifying headers, also update the [Usage Reporting Plugin](/apollo-server/api/plugin/usage-reporting) to use the new headers so that the proper client info is also sent to Studio.

### Ask clients to update their requests

The long-term fix will require that clients start sending the required headers needed to extract client info. While clients are working on updating their requests you can add the plugin code to your gateway, but instead of throwing an error you can log a warning so that the gateway team can track when all requests have been updated.
